/// <reference path="./../utils/Sets.ts" />
/// <reference path="./../utils/JS.ts" />

namespace apeng {

    export interface IStorage {
        clear(cb?: () => void): void
        getItem(key: string): string | null
        removeItem(key: string): void
        setItem(key: string, value: string): void
    }

    export interface IInstanceStorage {
        set(key: string | number, value: any, isDelay?: boolean): void
        get<T>(key: string | number, defaultValue: T): T
        delete(key: string | number): void
        getPrefixKeys: (str: string) => string[]
    }

    export class BaseInstanceStorage implements IInstanceStorage {

        constructor(
            public type: number,
            public storage: BaseStorage<number>,
        ) { }

        public set(key: string | number, value: any, isDelay?: boolean): void {
            this.storage.set(this.type, key, value, isDelay)
        }

        public get<T>(key: string | number, defaultValue: T): T {
            return this.storage.get(this.type, key, defaultValue)
        }

        public delete(key: string | number): void {
            this.storage.delete(this.type, key)
        }

        public getPrefixKeys(str: string) {
            return this.storage.getPrefixKeys(BaseStorage.getKey(this.type, str))
        }


    }


    export class BaseStorage<Type extends number> {
        public static getKey(type: number, key: string | number) { return (type as number) + "-" + key }

        public static parse(value: any) {
            if (value === undefined)
                return null!
            // 解析类型
            let type = (value as string)[0]
            value = (value as string).substring(1)
            switch (type) {
                case "b":
                    value = value == "true"
                    break
                case "n":
                    value = Number(value)
                    break
                case "o":
                    value = JSON.parse(value)
                    break
                case "s":
                    break
                default:
                    value = null!
                    break
            }
            return value
        }

        private cache: { [key: string]: any } = {}
        /**需要更新的缓存 */
        private updateCache: string[] = []

        constructor(
            /**缓存接口 */
            private localStorage: IStorage,
            /**延迟多长时间调用系统接口缓存(秒) */
            public delayCache: number = 1.2,
            public getObjData: () => Object,
        ) { }


        public byteSize(type: number) {
            let keys = this.getPrefixKeys(type + "-")
            let obj = <any>{}
            let orgin = this.getObjData() as any
            for (let key of keys)
                obj[key] = orgin[key]
            let str = JSON.stringify(obj)
            return (str.length / 1024).toFixed(2) + "kb"
        }

        public getPrefixKeys(prefix: string) {
            let obj = this.getObjData()
            let keys: string[] = []
            for (let key in obj)
                if (key.indexOf(prefix) == 0)
                    keys.push(key)
            return keys
        }

        private timeId = -1

        /**
         * 设置缓存
         * @param {string} key 
         * @param {string} value 与 get时类型保持一致 不能为 null undefined "null"
         * @param {boolean} isDelay 延迟缓存 
         * @param {boolean} toServer 推送到服务端 
         */
        public set(type: Type, key: string | number, value: any, isDelay: boolean = true) {
            key = BaseStorage.getKey(type, key)
            if (!this.checkCacheValue(value))
                return
            this.cache[key] = value
            Sets.add(this.updateCache, key)
            if (isDelay) {
                // 延迟调用系统缓存 避免高速存取耗费性能
                if (this.timeId != -1)
                    clearTimeout(this.timeId)
                this.timeId = setTimeout(this.onSet.bind(this), this.delayCache * 1000);
            }
            else
                this.onSet()
        }


        /**
         * 拿缓存 如没 返回 null
         * @param {string} key 
         * @param {T} defaultValue 返回当前值 
         * @param {boolean} save 默认没有缓存时 写入本地
         */
        public get<T>(type: Type, key: string | number, defaultValue: T): T {
            key = BaseStorage.getKey(type, key)
            let value = this.cache[key]
            // 当前没缓存
            if (!this.checkCacheValue(value)) {
                value = this.localStorage.getItem(key)
                // 系统也没缓存
                if (!this.checkCacheValue(value))
                    value = JS.deep(defaultValue)
                else {
                    value = BaseStorage.parse(value)
                    if (value === null!)
                        value = JS.deep(defaultValue)
                }
                this.cache[key] = value
            }
            return this.cache[key]
        }



        public has(type: Type, key: string | number): boolean {
            return !!this.get(type, key, null)
        }

        /**
         * 删除缓存
         * @param {string} key 
         */
        public delete(type: Type, key: string | number) {
            key = BaseStorage.getKey(type, key)
            delete this.cache[key]

            this.localStorage.removeItem(key)
        }

        /**清除全部缓存 */
        public clear(cb?: () => void) {
            this.cache = {}
            if (cb)
                this.localStorage.clear(cb)
            else
                this.localStorage.clear()
        }


        private checkCacheValue(value: any): boolean {
            if (value === null || value === "null" || value === undefined || value === "")
                return false
            return true
        }

        private onSet() {
            for (let key of this.updateCache) {
                let value = this.cache[key]
                let valueType = typeof value
                switch (valueType) {
                    case "boolean":
                        value = value + ""
                        break
                    case "number":
                        value = value + ""
                        break
                    case "object":
                        value = JSON.stringify(value)
                        break
                    case "string":
                        break
                    default:
                        value = null
                        break
                }
                if (value != null)
                    // 加上类型缓存 方便解析
                    this.localStorage.setItem(key, valueType[0] + value)
            }
            this.updateCache.length = 0
        }

    }
}
